<?php
/**
 * @todo Proper session data saving
 */
namespace TwinPeaks\Stdlib;
use TwinPeaks\Core as Core;

class CSession extends Core\CStdLib
{
	use Core\TSingleton, Core\TDecorator;
	
	/**
	 * @var string
	 */
	static private $tokenName = 'sessid';
	
	/**
	 * @var string
	 */
	static private $dataBaseDir = '/tmp/tp_data/';
	
	/**
	 * @var id Session Id
	 */
	private $id;
	
	/**
	 * @var object
	 */
	private $storageEngine;
	
	/**
	 * @var object
	 */
	private $user;
	
	/**
	 * @var integer
	 */
	private $userId;
	
	/**
	 * @var array
	 */
	private $vars;
	
	/**
	 * @var array
	 */
	private $flashMessages;
	
	/**
	 * @var array
	 */
	private $newFlashMessages = [];
	
	/**
	 * @var integer
	 */
	private $expireDate;
	
	/**
	 * @var integer
	 */
	private $refreshTime;

	/**
	 * @return string
	 */
	final static function getDataBaseDir()
	{
		return self::$dataBaseDir;
	}
	
	/**
	 * @return void
	 */
	final static function setDataBaseDir($pathname)
	{
		self::$dataBaseDir = $pathname;
	}
	
	/**
	 * @return
	 */
	protected function onCreate()
	{
		$this->id = self::getApplication()->getRequest()
			->getCookie(self::getTokenName());
		if ($this->id != null)
		{
			$dataRaw = $this->getStorage()->get($this->id);
			if ($dataRaw !== null)
			{
				$sessData = unserialize($dataRaw);
				$this->userId = $sessData['user_id'];
				$this->expireDate = $sessData['expire_date'];
				$this->vars = $sessData['vars'];
				$this->flashMessages = $sessData['flash_msg'];
				$this->refreshTime = time();
				return;
			}
		}
		$this->init();
	}
	
	/**
	 * @return void
	 */
	function __destruct()
	{
		if ($this->isActive())
			$this->save();
	}
	
	/**
	 * @return void
	 */
	function init()
	{
		$this->userId = null;
		$this->vars = [];
		$this->flashMessages = [];
		$this->newFlashMessages = [];
	}
	
	/**
	 * @return boolean
	 */
	final function isActive()
	{
		return $this->userId === null ? false : true;
	}

	/**
	 * @return void
	 */
	final function end()
	{
		$this->init();
		$this->getStorage()->remove($this->id); // Remove session data
		$this->getApplication()->getResponse()->removeCookie(static::getTokenName());		
	}
	
	/**
	 * Flushes session data
	 * 
	 * @return void
	 */
	final function flushData()
	{
		$this->vars = [];
		$this->newFlashMessages = [];
	}
	
	/**
	 * @return mixed
	 */
	final function get($name)
	{
		if (!$this->isActive())
			return null;
		return isset($this->vars[$name]) ? $this->vars[$name] : null;
	}
	
	/**
	 * @return CSession
	 */
	final function set($name, $value)
	{		
		$this->vars[$name] = $value;
		return $this;
	}

	/**
	 * @return
	 */
	final function getStorage()
	{
		if (isset($this->storageEngine))
			return $this->storageEngine;

		$self = Core\get_class_method_name(__METHOD__);
		if ($this->hasDecoration($self))
		{
			$engine = $this->renderDecoration($self);
			if (!($engine instanceof Storage\IKeyValue))
				Core\CException::interfaceNotImplemented($engine, 'Storage\IKeyValue')
					->_throw();
		}
		else // Use default filesystem storage
		{
			$this->getApplication()->loadClassByName('@F/lib/storage/kv/', 'CFilesystem');
			$this->storageEngine = new Storage\KeyValue\CFilesystem(self::getDataBaseDir(),
				self::getTokenName() . '_');

			return $this->storageEngine;
		}
	}
	
	/**
	 * @return string|null
	 */
	function getId()
	{
		return $this->id;
	}
	
	/**
	 * 
	 */
	final static function setTokenName($token)
	{
		self::$tokenName = $token;
	}
	
	/**
	 * @return string
	 */
	final static function getTokenName()
	{
		return self::$tokenName;
	}
	
	/**
	 * Generates session Id
	 * 
	 * @return string
	 */
	function generateId()
	{
		return $this->getApplication()->getHostname(). '_' . Core\get_unique_string();
	}
	
	/**
	 * @return integer
	 */
	final function getUserId()
	{
		return $this->userId;
	}
	
	/**
	 * @return CUser|null 
	 */
	final function getUser()
	{
		if (isset($this->user))
			return $this->user;
		
		$self = Core\get_class_method_name(__METHOD__);
		if ($this->hasDecoration($self))
		{
			$this->user = $this->renderDecoration($self);
			if (!($this->user instanceof Core\IUser))
				Core\CException::interfaceNotImplemented($this->user, 'IUser')
					->_throw();
			return $this->user;
		}
		else
			return null;
	}
	
	/**
	 * @return void
	 */
	final function assignUser($callback)
	{
		$this->decorateWith(self::DECORABLE_METHOD_GET_USER, $callback);
	}

	/**
	 * @return void
	 */
	final function start($userId = -1, $expireMixed = 0, $path = '', $domain = '', $secure = '')
	{
		$this->init();
		$this->userId = $userId;
		$this->id = $this->generateId();
		$this->expireDate = $this->getApplication()->getResponse()->setCookie(static::getTokenName(),
			$this->getId(), $expireMixed, $path, $domain, $secure, true);
	}
	
	/**
	 * @return string
	 */
	function getFlashMessage($msgId)
	{
		return isset($this->flashMessages[$msgId]) ?
			$this->flashMessages[$msgId] : '';
	}
	
	/**
	 * @return void
	 */
	function setFlashMessage($msgId, $value = true)
	{
		$this->newFlashMessages[$msgId] = $value;
	}
	
	/**
	 * @return boolean
	 */
	function hasFlashMessage($msgId)
	{
		return isset($this->flashMessages[$msgId]) ? true : false;		
	}

	/**
	 * @return void
	 */
	final function save()
	{
		$dataBulk = [
			'user_id' => $this->userId,
			'vars' => $this->vars,
			'expire_date' => $this->expireDate,
			'flash_msg' => $this->newFlashMessages,
			'refresh_time' => $this->refreshTime
		]; 
		
		$this->getStorage()->set($this->getId(), serialize($dataBulk));
	}
}
